﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Windows.ApplicationModel.Resources.Core;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging;
using System.Collections.Specialized;
using InfoReaderLib.Info;
using InfoReaderLib.Parser;
using System.Threading.Tasks;
using System.Runtime.InteropServices.WindowsRuntime;
using InfoReaderWin8RT.Common;
using InfoReaderLib;

// The data model defined by this file serves as a representative example of a strongly-typed
// model that supports notification when members are added, removed, or modified.  The property
// names chosen coincide with data bindings in the standard item templates.
//
// Applications may use this model as a starting point and build on it, or discard it entirely and
// replace it with something appropriate to their needs.

namespace InfoReaderWin8RT.Data
{
    /// <summary>
    /// Base class for <see cref="DisplayDataItem"/> and <see cref="DisplayDataSource"/> that
    /// defines properties common to both.
    /// </summary>
    [Windows.Foundation.Metadata.WebHostHidden]
    public abstract class DisplayDataCommon : InfoReaderWin8RT.Common.BindableBase
    {
        private static Uri _baseUri = new Uri("ms-appx:///");

        public DisplayDataCommon(String uniqueId, String title, String subtitle, String imagePath, String description)
        {
            this._uniqueId = uniqueId;
            this._title = title;
            this._subtitle = subtitle;
            this._description = description;
            this._imagePath = imagePath;
        }

        private string _uniqueId = string.Empty;
        public string UniqueId
        {
            get { return this._uniqueId; }
            set { this.SetProperty(ref this._uniqueId, value); }
        }

        private string _title = string.Empty;
        public string Title
        {
            get { return this._title; }
            set { this.SetProperty(ref this._title, value); }
        }

        private string _subtitle = string.Empty;
        public string Subtitle
        {
            get { return this._subtitle; }
            set { this.SetProperty(ref this._subtitle, value); }
        }

        private string _description = string.Empty;
        public string Description
        {
            get { return this._description; }
            set { this.SetProperty(ref this._description, value); }
        }

        private ImageSource _image = null;
        private String _imagePath = null;
        public ImageSource Image
        {
            get
            {
                if (this._image == null && this._imagePath != null)
                {
                    this._image = new BitmapImage(new Uri(DisplayDataCommon._baseUri, this._imagePath));
                }
                return this._image;
            }

            set
            {
                this._imagePath = null;
                this.SetProperty(ref this._image, value);
            }
        }

        public void SetImage(String path)
        {
            this._image = null;
            this._imagePath = path;
            this.OnPropertyChanged("Image");
        }

        public override string ToString()
        {
            return this.Title;
        }
    }

    /// <summary>
    /// Generic item data model.
    /// </summary>
    public class DisplayDataItem : DisplayDataCommon
    {
        public DisplayDataItem(String uniqueId, String title, String subtitle, String imagePath, String description, String content, DisplayDataSource group, InfoDetail infoDetail)
            : base(uniqueId, title, subtitle, imagePath, description)
        {
            this._content = content;
            this._group = group;
            this._infoDetail = infoDetail;
        }

        private string _content = string.Empty;
        public string Content
        {
            get { return this._content; }
            set { this.SetProperty(ref this._content, value); }
        }

        /// <summary>
        /// Parent group which this item belongs to
        /// </summary>
        private DisplayDataSource _group;
        /// <summary>
        /// Parent group which this item belongs to
        /// </summary>
        public DisplayDataSource Group
        {
            get { return this._group; }
            set { this.SetProperty(ref this._group, value); }
        }

        private InfoDetail _infoDetail;

        public InfoDetail InfoDetail
        {
            get { return this._infoDetail; }
            set { this.SetProperty(ref this._infoDetail, value); }
        }

        /// <summary>
        /// Refresh content
        /// </summary>
        /// <returns></returns>
        public IAsyncAction Refresh()
        {
            return AsyncInfo.Run(async _ =>
            {
                try
                {
                    IInfo parsedInfo = await this.Group.InfoReader.Parse(this.InfoDetail);
                    InfoDetail parsedDetail = (InfoDetail)parsedInfo;
                    this.InfoDetail = parsedDetail;
                    this.Content = parsedDetail.Article;
                }
                catch (Exception ex)
                {
                    Util.Debug(ex);
                }
            });
        }
    }

    /// <summary>
    /// Generic group data model.
    /// </summary>
    public class DisplayDataSource : DisplayDataCommon
    {
        public DisplayDataSource(String uniqueId, String title, String subtitle, String imagePath, String description, DisplayCategory category)
            : base(uniqueId, title, subtitle, imagePath, description)
        {
            Items.CollectionChanged += ItemsCollectionChanged;
            this._category = category;
        }

        private void ItemsCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            // Provides a subset of the full items collection to bind to from a GroupedItemsPage
            // for two reasons: GridView will not virtualize large items collections, and it
            // improves the user experience when browsing through groups with large numbers of
            // items.
            //
            // A maximum of 12 items are displayed because it results in filled grid columns
            // whether there are 1, 2, 3, 4, or 6 rows displayed

            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    if (e.NewStartingIndex < 12)
                    {
                        TopItems.Insert(e.NewStartingIndex,Items[e.NewStartingIndex]);
                        if (TopItems.Count > 12)
                        {
                            TopItems.RemoveAt(12);
                        }
                    }
                    break;
                case NotifyCollectionChangedAction.Move:
                    if (e.OldStartingIndex < 12 && e.NewStartingIndex < 12)
                    {
                        TopItems.Move(e.OldStartingIndex, e.NewStartingIndex);
                    }
                    else if (e.OldStartingIndex < 12)
                    {
                        TopItems.RemoveAt(e.OldStartingIndex);
                        TopItems.Add(Items[11]);
                    }
                    else if (e.NewStartingIndex < 12)
                    {
                        TopItems.Insert(e.NewStartingIndex, Items[e.NewStartingIndex]);
                        TopItems.RemoveAt(12);
                    }
                    break;
                case NotifyCollectionChangedAction.Remove:
                    if (e.OldStartingIndex < 12)
                    {
                        TopItems.RemoveAt(e.OldStartingIndex);
                        if (Items.Count >= 12)
                        {
                            TopItems.Add(Items[11]);
                        }
                    }
                    break;
                case NotifyCollectionChangedAction.Replace:
                    if (e.OldStartingIndex < 12)
                    {
                        TopItems[e.OldStartingIndex] = Items[e.OldStartingIndex];
                    }
                    break;
                case NotifyCollectionChangedAction.Reset:
                    TopItems.Clear();
                    while (TopItems.Count < Items.Count && TopItems.Count < 12)
                    {
                        TopItems.Add(Items[TopItems.Count]);
                    }
                    break;
            }
        }

        private ObservableCollection<DisplayDataItem> _items = new ObservableCollection<DisplayDataItem>();
        public ObservableCollection<DisplayDataItem> Items
        {
            get { return this._items; }
        }

        private ObservableCollection<DisplayDataItem> _topItem = new ObservableCollection<DisplayDataItem>();
        public ObservableCollection<DisplayDataItem> TopItems
        {
            get {return this._topItem; }
        }

        /// <summary>
        /// Parent category which this item belongs to
        /// </summary>
        private DisplayCategory _category;
        /// <summary>
        /// Parent group which this item belongs to
        /// </summary>
        public DisplayCategory Category
        {
            get { return this._category; }
            set { this.SetProperty(ref this._category, value); }
        }

        public IInfoSource InfoSource { get; set; }

        public string ParserSetting { get; set; }

        public Reader InfoReader { get; set; }

        /// <summary>
        /// Download latest items from InfoSource
        /// </summary>
        public IAsyncAction RefreshItems()
        {
            return AsyncInfo.Run(async _ =>
            {
                try
                {
                    if (this.InfoSource == null) return;

                    if (this.InfoReader == null)
                    {
                        // initialize reader
                        this.InfoReader = new Reader()
                        {
                            Source = this.InfoSource,
                            ParserSetting = this.ParserSetting
                        };
                        this.InfoReader.Init();
                    }

                    IInfo returnInfo = await this.InfoReader.Parse();

                    // push result to UI thread
                    if (returnInfo == null) return;

                    // clear all first
                    Items.Clear();

                    // insert
                    var infoList = (IInfoList)returnInfo;
                    foreach (InfoDetail listItem in infoList.List)
                    {
                        Util.Debug("Item Author=" + listItem.Author);
                        Util.Debug("Item CreateDateText=" + listItem.CreateDateText);
                        Util.Debug("Item Title=" + listItem.Title);
                        Util.Debug("Item Url=" + listItem.Url);

                        Items.Add(new DisplayDataItem(
                            listItem.Url,
                            listItem.Title,
                            listItem.Author,
                            null,
                            Description = listItem.CreateDateText,
                            null,
                            this,
                            listItem
                        ));
                    }
                }
                catch (Exception ex)
                {
                    Util.Debug(ex);
                }
            });
        }

        public DisplayDataItem GetDataItem(string url)
        {
            var dataItem = (from i in this.Items
                              where i.UniqueId == url
                              select i).FirstOrDefault();

            return dataItem;
        }
    }

    public class DisplayCategory : DisplayDataCommon
    {
        public DisplayCategory(String uniqueId, String title, String subtitle, String imagePath, String description)
            : base(uniqueId, title, subtitle, imagePath, description)
        {
            Items.CollectionChanged += ItemsCollectionChanged;
        }

        private void ItemsCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            // Provides a subset of the full items collection to bind to from a GroupedItemsPage
            // for two reasons: GridView will not virtualize large items collections, and it
            // improves the user experience when browsing through groups with large numbers of
            // items.
            //
            // A maximum of 12 items are displayed because it results in filled grid columns
            // whether there are 1, 2, 3, 4, or 6 rows displayed

            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    if (e.NewStartingIndex < 12)
                    {
                        TopItems.Insert(e.NewStartingIndex, Items[e.NewStartingIndex]);
                        if (TopItems.Count > 12)
                        {
                            TopItems.RemoveAt(12);
                        }
                    }
                    break;
                case NotifyCollectionChangedAction.Move:
                    if (e.OldStartingIndex < 12 && e.NewStartingIndex < 12)
                    {
                        TopItems.Move(e.OldStartingIndex, e.NewStartingIndex);
                    }
                    else if (e.OldStartingIndex < 12)
                    {
                        TopItems.RemoveAt(e.OldStartingIndex);
                        TopItems.Add(Items[11]);
                    }
                    else if (e.NewStartingIndex < 12)
                    {
                        TopItems.Insert(e.NewStartingIndex, Items[e.NewStartingIndex]);
                        TopItems.RemoveAt(12);
                    }
                    break;
                case NotifyCollectionChangedAction.Remove:
                    if (e.OldStartingIndex < 12)
                    {
                        TopItems.RemoveAt(e.OldStartingIndex);
                        if (Items.Count >= 12)
                        {
                            TopItems.Add(Items[11]);
                        }
                    }
                    break;
                case NotifyCollectionChangedAction.Replace:
                    if (e.OldStartingIndex < 12)
                    {
                        TopItems[e.OldStartingIndex] = Items[e.OldStartingIndex];
                    }
                    break;
                case NotifyCollectionChangedAction.Reset:
                    TopItems.Clear();
                    while (TopItems.Count < Items.Count && TopItems.Count < 12)
                    {
                        TopItems.Add(Items[TopItems.Count]);
                    }
                    break;
            }
        }

        private ObservableCollection<DisplayDataSource> _items = new ObservableCollection<DisplayDataSource>();
        public ObservableCollection<DisplayDataSource> Items
        {
            get { return this._items; }
        }

        private ObservableCollection<DisplayDataSource> _topItem = new ObservableCollection<DisplayDataSource>();
        public ObservableCollection<DisplayDataSource> TopItems
        {
            get { return this._topItem; }
        }

        public DisplayDataSource GetDataSource(string datasourceId)
        {
            var dataSource = (from i in this.Items
                              where i.UniqueId == datasourceId
                              select i).FirstOrDefault();
            return dataSource;
        }
    }

    /// <summary>
    /// Root class storing all loaded data
    /// 
    /// Initializes with placeholder data rather than live production
    /// data so that Display data is provided at both design-time and run-time.
    /// </summary>
    public sealed class MainDataModel
    {
        private static MainDataModel _MainDataModel = new MainDataModel();

        private bool IsInitialized = false;

        private ObservableCollection<DisplayCategory> _allGroups = new ObservableCollection<DisplayCategory>();
        public ObservableCollection<DisplayCategory> AllGroups
        {
            get { return this._allGroups; }
        }

        public static IEnumerable<DisplayCategory> GetGroups(string uniqueId)
        {
            if (!uniqueId.Equals("AllGroups")) throw new ArgumentException("Only 'AllGroups' is supported as a collection of groups");

            return _MainDataModel.AllGroups;
        }

        public static DisplayCategory GetGroup(string uniqueId)
        {
            // Simple linear search is acceptable for small data sets
            var matches = _MainDataModel.AllGroups.Where((group) => group.UniqueId.Equals(uniqueId));
            if (matches.Count() == 1) return matches.First();
            return null;
        }

        /// <summary>
        /// Initialize available data sources from store
        /// </summary>
        /// <returns></returns>
        public static IAsyncAction Init()
        {
            return AsyncInfo.Run(async _ =>
            {
                if (_MainDataModel.IsInitialized)
                    return;

                string parserSetting = null;

                // welovewphk
                parserSetting = await Util.FillJSONObjectFromAnotherFileAsync(
                    @"Data\Source\welovewphk\SettingJSON.txt",
                    new Dictionary<string, string>(){
                        {"ListRegEx", @"Data\Source\welovewphk\RegExList.txt"},
                        {"DetailRegEx", @"Data\Source\welovewphk\RegExDetail.txt"}
                    });
                Util.Debug(parserSetting);

                var cat1 = new DisplayCategory("1",
                        "Techology",
                        "",
                        "Assets/DarkGray.png",
                        "");
                cat1.Items.Add(new DisplayDataSource("1",
                        "We Love Windows Phone.HK",
                        "1",
                        "Assets/LightGray.png",
                        "",
                        cat1)
                {
                    InfoSource = new InfoSource()
                    {
                        ParserName = "SimpleParser",
                        Url = "http://welovewp.hk/"
                    },
                    ParserSetting = parserSetting
                });

                _MainDataModel.AllGroups.Add(cat1);
                _MainDataModel.IsInitialized = true;
            });
        }
    }
}
